﻿using System;
using System.Collections.Generic;
using System.Windows;
#if WPF
using System.Windows.Controls;
using System.Windows.Shapes;
#else
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Shapes;
using Windows.Foundation;
using Windows.UI.Xaml;
#endif

namespace Eternal.DataVisualization
{
    public class XAxis : Axis
    {
        public PositionInAreaX PositionInArea
        {
            get { return (PositionInAreaX)GetValue(PositionInAreaProperty); }
            set { SetValue(PositionInAreaProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PositionInArea.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PositionInAreaProperty =
            DependencyProperty.Register("PositionInArea", typeof(PositionInAreaX), typeof(XAxis), new PropertyMetadata(PositionInAreaX.Bottom));

        protected override void IterateData()
        {
            if (AxisData != null)
            {
                padding = true;
                var enumerator = AxisData.GetEnumerator();
                object val = null;
                var i = 0;
                if (enumerator.MoveNext())
                {
                    val = enumerator.Current;
                    ConvertedData = new List<double>();
                    if (val is string)
                    {
                        ActualData = new List<string>();
                        do
                        {
                            ActualData.Add(enumerator.Current);
                            ConvertedData.Add(i);
                            i++;
                        } while (enumerator.MoveNext());
                        Type = "String";
                    }
                    else if (val is DateTime)
                    {
                        ActualData = new List<DateTime>();
                        do
                        {
                            var currVal = (DateTime)enumerator.Current;
                            ActualData.Add(enumerator.Current);
                            ConvertedData.Add(currVal.ToOADate());
                        } while (enumerator.MoveNext());
                        Type = "DateTime";
                    }
                    else
                    {
                        ActualData = new List<double>();
                        do
                        {
                            ActualData.Add(enumerator.Current);
                            ConvertedData.Add(i);
                            i++;
                        } while (enumerator.MoveNext());
                        Type = "Double";
                    }
                    UpdateMinimumMaximum();
                    ActualDelta = ActualEnd - ActualStart;
                    //Start = ActualStart;
                    //End = ActualEnd;
                }
            }
        }

        protected override void CalculateInterval()
        {
            if (Interval == null)
                //ActualInterval = Math.Ceiling(ActualData.Count / (ActualSize.Width / 100));
                ActualInterval = 1; //ActualDelta / ActualData.Count;
            else
                ActualInterval = Convert.ToDouble(Interval);

        }

        protected override void ApplyPadding()
        {
            Start -= 1;
            End += 1;
            base.ApplyPadding();
        }

        protected override Size MeasureOverride(Size constraint)
        {
            if (!double.IsPositiveInfinity(constraint.Width))
            {
                if (constraint.Width != ActualSize.Width)
                    CanUpdate = true;
                ActualSize.Width = constraint.Width;
            }
            return base.MeasureOverride(ActualSize);
        }

        protected override void GenerateRecycleElement()
        {
            
            TickPresenter.Width = LabelPresenter.Width = ActualSize.Width;
            TickPresenter.Height = OuterTickSize;
            LabelPresenter.Height = maxHeight;
            var axislabelEnum = AxisLabelRecycler.RecycleChildren(Labels.Count).GetEnumerator();
            var tickElementEnum = TickRecycler.RecycleChildren(Labels.Count).GetEnumerator();
            Line line = null;
            Point position;
            double actualElementSize = 0;
            for (var i = 0; i < Labels.Count; i++)
            {
                if (tickElementEnum.MoveNext())
                    line = tickElementEnum.Current;
                line.Style = OuterTickStyle;
                ContentPresenter control = null;
                if (axislabelEnum.MoveNext())
                {
                    control = axislabelEnum.Current;
                    control.Content = null;
                    control.Content = Labels[i].Content;
                    Labels[i].Element = control;
                    Labels[i].Element.Measure(tempMeasureSize);
                    actualElementSize = control.DesiredSize.Width;
                }

                line.Y2 = OuterTickSize;

                position = Labels[i].Position ;

                line.SetValue(Canvas.LeftProperty, position.X);
                line.SetValue(Canvas.TopProperty, position.Y);

                control.SetValue(Canvas.LeftProperty, position.X - (actualElementSize / 2));
                control.SetValue(Canvas.TopProperty, position.Y);
            }
            AxisLine.X2 = ActualSize.Width;
        }

        protected override void GenerateLabelsAndSize()
        {
            Labels.Clear();
            if (Type == "String")
                GenerateStringLabels();
            else if (Type == "Double")
                GenerateDoubleLabels();
            else if (Type == "DateTime")
                GenerateDateTimeLabels();


            AxisLine.Measure(tempMeasureSize);
            HeaderPresenter.Measure(tempMeasureSize);
            ActualSize.Height = HeaderPresenter.DesiredSize.Height + maxHeight + OuterTickSize + AxisLine.DesiredSize.Height;
        }

        double maxHeight;
        void GenerateStringLabels()
        {
            AxisLabel label;
            double previousPos = 0;
            for (var i = (int)Start; i <= End; i = i + (int)Math.Ceiling(ActualInterval))
            {
                if (ActualData.Count <= i || i < 0) continue;
                label = new AxisLabel();
                label.Position = new Point(Math.Round(ActualSize.Width * ((i - Start) / ActualDelta)),
                              0);
                if (previousPos > label.Position.X)
                {
                    continue;
                }
                label.ActualContent = i;
                contentContrl.Content = label.Content = ActualData[i];
                contentContrl.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                Labels.Add(label);

                var actualValue = contentContrl.DesiredSize.Height;
                maxHeight = maxHeight < actualValue ? actualValue : maxHeight;
                previousPos = label.Position.X + contentContrl.DesiredSize.Width;
            }
        }

        void GenerateDoubleLabels()
        {
            AxisLabel label;
            double previousPos = 0;
            for (var i = (int)Start; i <= End; i = i + (int)Math.Ceiling(ActualInterval))
            {
                label = new AxisLabel();
                label.Position = GetPoint(this, i);
                if (previousPos > label.Position.X)
                {
                    continue;
                }
                label.ActualContent = i;
                contentContrl.Content = label.Content = ActualData[i];
                contentContrl.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                Labels.Add(label);

                var actualValue = contentContrl.DesiredSize.Height;
                maxHeight = maxHeight < actualValue ? actualValue : maxHeight;
                previousPos = label.Position.X + contentContrl.DesiredSize.Width;
            }
        }

        public override Point GetPoint(Axis axis, double value)
        {
            return new Point(Math.Round(axis.ActualSize.Width * ((value - axis.Start) / axis.ActualDelta)),
                              0);
        }

        protected override void AddToVisual()
        {
            if (AxisPosition == Position.InSideArea)
            {
                foreach (var item in Presenters)
                {
                    RootPanel.Children.Add(item);
                }
                RootPanel.Children.Add(AxisLine);
            }
            else
            {
                RootPanel.Children.Add(AxisLine);
                for (var i = Presenters.Count - 1; i >= 0; i--)
                {
                    RootPanel.Children.Add(Presenters[i]);
                }
            }
        }

        void GenerateDateTimeLabels()
        {
            AxisLabel label;
            double previousPos = 0;
            var j = 0;
            for (var i = (int)Start; i <= (int)End; i += (int)Math.Ceiling(ActualInterval))
            {
                label = new AxisLabel();
                label.Position = new Point(Math.Round(ActualSize.Width * ((i - ActualStart) / ActualDelta)),
                              0);
                if (previousPos > label.Position.X)
                {
                    j++;
                    continue;
                }
                label.ActualContent = i;
                contentContrl.Content = label.Content = ActualData[j];
                contentContrl.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                Labels.Add(label);

                var actualValue = contentContrl.DesiredSize.Height;
                maxHeight = maxHeight < actualValue ? actualValue : maxHeight;
                previousPos = label.Position.X + contentContrl.DesiredSize.Width;
                j++;
            }
        }

#if WPF
        public override void OnApplyTemplate()
#else
        protected override void OnApplyTemplate()
#endif
        {
            HeaderPresenter = new ContentPresenter();
            Labels = new List<AxisLabel>();
            RootPanel = GetTemplateChild("PART_Root") as StackPanel;
            Presenters = new List<UIElement>();
            LabelPresenter = new Canvas();
            TickPresenter = new Canvas();
            SmallTickPresenter = new Canvas();
            AxisLine = new Line();
            AxisLine.Style = AxisLineStyle;
            HeaderPresenter.Content = Header;
            Presenters.Add(HeaderPresenter);
            HeaderPresenter.HorizontalAlignment = HorizontalAlignment.Center;
            Presenters.Add(LabelPresenter);
            Presenters.Add(TickPresenter);
            //Presenters.Add(SmallTickPresenter);
            //Presenters.Add(AxisLine);
            AxisLabelRecycler = new ElementRecycler<ContentPresenter>(LabelPresenter);
            TickRecycler = new ElementRecycler<Line>(TickPresenter);
            base.OnApplyTemplate();
            AddToVisual();
        }
    }
}
